package permission

import (
	context "context"
	"errors"
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"github.com/ifnk/micro-blog/internal/pkg/dbcontext"
	"github.com/ifnk/micro-blog/internal/pkg/log"
	"github.com/ifnk/micro-blog/protobuf/permission_pb"
	"sync"
)

var (
	syncedEnforcer *casbin.SyncedEnforcer
	once           sync.Once
)

func NewCasbinServer(logger *log.Logger, db *dbcontext.GormDB, jinzhuDB *dbcontext.JinZhuDB) CasbinServer {
	return CasbinServer{
		logger: logger,
		db:     db,
	}
}

type CasbinServer struct {
	permission_pb.UnimplementedCasbinServiceServer
	logger *log.Logger
	db     *dbcontext.GormDB
}

func (c CasbinServer) CheckExistsByClaim(ctx context.Context, request *permission_pb.CheckExistsByClaimRequest) (*permission_pb.EmptyResponse, error) {
	obj := request.Path
	act := request.Method
	ids := request.AuthorityIds
	for _, sub := range ids {
		e := c.Casbin()
		success, _ := e.Enforce(sub, obj, act)
		if success {
			return &permission_pb.EmptyResponse{Success: true}, nil
		}
	}
	return &permission_pb.EmptyResponse{Success: false}, nil
}

func (c CasbinServer) ClearCasbin(v int, p ...string) bool {
	e := c.Casbin()
	success, _ := e.RemoveFilteredPolicy(v, p...)
	return success
}

func (c CasbinServer) Casbin() *casbin.SyncedEnforcer {
	once.Do(func() {
		a, _ := gormadapter.NewAdapterByDB(c.db.DB)
		syncedEnforcer, _ = casbin.NewSyncedEnforcer("./configs/casbin.conf", a)
	})
	_ = syncedEnforcer.LoadPolicy()
	return syncedEnforcer
}

func (c CasbinServer) ModifyCasbin(ctx context.Context, request *permission_pb.UpdateCasbinRequest) (*permission_pb.EmptyResponse, error) {
	c.ClearCasbin(0, request.AuthorityId)
	var rules [][]string
	for _, v := range request.CasbinInfos {
		rules = append(rules, []string{request.AuthorityId, v.Path, v.Method})
	}
	e := c.Casbin()
	success, _ := e.AddPolicies(rules)
	if !success {
		return nil, errors.New("存在相同api,添加失败,请联系管理员")
	}
	return &permission_pb.EmptyResponse{Success: true}, nil
}

func (c CasbinServer) ModifyApi(ctx context.Context, request *permission_pb.UpdateCasbinApiRequest) (*permission_pb.EmptyResponse, error) {
	oldPath := request.OldPath
	newPath := request.NewPath
	oldMethod := request.OldMethod
	newMethod := request.NewMethod
	err := c.db.Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{
		"v1": newPath,
		"v2": newMethod,
	}).Error
	return &permission_pb.EmptyResponse{Success: true}, err
}

func (c CasbinServer) GetPolicyPathByAuthorityId(ctx context.Context, request *permission_pb.GetPolicyPathByAuthorityIdRequest) (*permission_pb.GetPolicyPathByAuthorityIdResponse, error) {
	var pathMaps []*permission_pb.CasbinInfo
	authorityId := request.AuthorityId
	e := c.Casbin()
	list := e.GetFilteredPolicy(0, authorityId)
	for _, v := range list {
		pathMaps = append(pathMaps, &permission_pb.CasbinInfo{
			Path:   v[1],
			Method: v[2],
		})
	}
	return &permission_pb.GetPolicyPathByAuthorityIdResponse{CasbinInfos: pathMaps}, nil
}

func (c CasbinServer) CleanCasbin(ctx context.Context, request *permission_pb.CleanCasbinRequest) (*permission_pb.CleanCasbinResponse, error) {
	v := request.V
	p := request.P
	e := c.Casbin()
	success, err := e.RemoveFilteredPolicy(int(v), p...)
	return &permission_pb.CleanCasbinResponse{Success: success}, err
}
